/**
 * 版权 (c) 2013 意定达科技有限公司
 * 保留所有权利。
 */
package cn.lunatic.xmlpares;

import java.io.File;
import java.io.StringReader;
import java.util.List;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Text;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;

import cn.lunatic.utils.StringUtils;

/**
 * @Description:XMLJdom解析工具
 * @author ganfeng
 * @date 2014-3-6 下午03:44:17
 */
public class JdomXMLUtil {
	private final static Logger logger = Logger.getLogger(JdomXMLUtil.class);// 获取
	static final String ATTR_NAME_DEF = "id"; // 缺省的属性ID,多个相同标签的节点,可作为唯一值判断
	static final String ROOT_NAME = "lunatic";

	private Document doc;
	private Element root;

	/**
	 * 
	 * @Description:获取doc
	 * @author ganfeng
	 * @date 2014-3-7 上午11:26:50
	 * @return
	 */
	public Document getDocument() {
		return doc;
	}
	/**
	 * 
	 * @Description:获取root
	 * @author ganfeng
	 * @date 2014-3-7 上午11:27:02
	 * @return
	 */
	public Element getRoot() {
		return root;
	}

	/**
	 * 
	 * @Description:初始分析xml Element
	 * @author ganfeng
	 * @date 2014-3-7 上午11:27:38
	 * @param node
	 * @return
	 */
	public Element parse(Element node) {
		root = node;
		return root;
	}

	/**
	 * 
	 * @Description: 初始分析xml字符串,返回Element
	 * @author ganfeng
	 * @date 2014-3-7 上午11:29:17
	 * @param xmlStr xml文本串
	 * @return
	 */
	public Element parse(String xmlStr) {
		try {
			SAXBuilder builder = new SAXBuilder();
			doc = builder.build(new StringReader(xmlStr));
			root = doc.getRootElement();
		} catch (Exception e) {
			logger.info("解释xml数据出错:" + xmlStr, e);
		}
		return root;
	}
	
	/**
	 * 
	 * @Description: 解释xml字符串,返回Element
	 * @author ganfeng
	 * @date 2014-3-7 上午11:30:12
	 * @param xmlStr xml文本串
	 * @return
	 */
	public static Element parseToElement(String xmlStr) {
		Element root = null;
		try {
			SAXBuilder builder = new SAXBuilder();
			Document doc = builder.build(new StringReader(xmlStr));
			root = doc.getRootElement();
		} catch (Exception e) {
			logger.info("解释xml数据出错:" + xmlStr, e);
		}
		return root;
	}

	/**
	 * 
	 * @Description: 将指定的xml文件解释成Element
	 * @author ganfeng
	 * @date 2014-3-7 上午11:30:47
	 * @param xmlFile 文件名
	 * @return
	 */
	public Element parseFile(String xmlFile) {
		try {
			SAXBuilder builder = new SAXBuilder();
			Document doc = builder.build(xmlFile);
			root = doc.getRootElement();
			removeNameSapce(); // 去除命名空间
		} catch (Exception e) {
			logger.info("解释xml文件出错:" + xmlFile, e);
		}
		return root;
	}

	/**
	 * 
	 * @Description:去除ROOT_NAME根节点命名空间
	 * @author ganfeng
	 * @date 2014-3-7 上午11:41:15
	 */
	private void removeNameSapce() {
		if (root == null) {
			return;
		}
		String nameSpace = root.getNamespaceURI();
		if (StringUtils.isBlank(nameSpace)) {
			return;
		}
		try {
			String string = JdomXMLUtil.formatXml(root);
			String regEx = "<" + ROOT_NAME + "[^>]+>";
			Pattern p = Pattern.compile(regEx);
			Matcher m = p.matcher(string);
			String s = m.replaceAll("<" + ROOT_NAME + ">");

			System.out.println(s);
			
			parse(s);
		} catch (Exception e) {
			logger.info("去除xml命名空间出错", e);
		}

	}


	/**
	 * 
	 * @Description:递归添加一个目录下的所有xml文件
	 * @author ganfeng
	 * @date 2014-3-7 上午11:46:34
	 * @param dir 目录路径
	 */
	public void addDir(String dir) {
		if (!StringUtils.isBlank(dir)) {
			File configFile = new File(dir);
			if (!configFile.exists()) {
				logger.info("文件" + "：(" + dir + ") 不存在");
				throw new RuntimeException("文件" + "：(" + dir + ") 不存在");
			}
			recuAddDir(new File(dir));
		} else {
			throw new RuntimeException("文件不能为空串！");
		}
	}

	/**
	 * @Description:递归查询目录添加XML文件
	 * @author ganfeng
	 * @date 2014-3-7 上午11:47:34
	 * @param dir
	 */
	private void recuAddDir(File dir) {
		File[] listFiles = dir.listFiles();
		for (int i = 0; i < listFiles.length; i++) {
			File file = listFiles[i];
			if (file.isDirectory()) {// 如果是目录，继续递归
				recuAddDir(file);
			} else if (file.isFile()) {// 如果是XML文件
				String filePath = file.getPath();
				if (filePath.endsWith(".xml")) {
					logger.info("add file: " + filePath);
					JdomXMLUtil jdom = new JdomXMLUtil();
					Element newRoot = jdom.parseFile(filePath);
					recuAddNode(root, newRoot);
				}
			}
		}
	}
	
	/**
	 * 
	 * @Description: 将eNew节点合并到eMain
	 * @author ganfeng
	 * @date 2014-3-7 上午11:49:41
	 * @param eMain
	 * @param eNew
	 */
	@SuppressWarnings("unchecked")
	private void recuAddNode(Element eMain, Element eNew) {
		List<Element> children = eNew.getChildren();
		int i = 0;
		for (;;) {
			if (i >= children.size()) {// 检查节点是否遍历完
				break;
			}
			// 取主类的克隆子类
			Element child = children.get(i);
			Element mainCloneChild = getMainCloneChild(eMain, child);
			if (mainCloneChild != null) {// 如果存在克隆子类,则递归进子类重新比较,否则,直接将子节点加入主节点树
				i++;
				recuAddNode(mainCloneChild, child);
			} else {// 注意: 节点分离后,children.size()会发生变化
				child.detach();
				eMain.addContent(child);
			}
		}
	}
	
	/**
	 * 
	 * @Description: 功能：判断当前节点,是否是主类父节点下的能共存的兄弟节点 
	 * 					1.当前节点无属性值,如果节点名与主类父节点的孩子节点名相同,则不是能共存的兄弟节点
	 * 					2.节点属性中，id属性相同的，抛出异常 
	 * 					3.其他都是能共存的
	 * @author ganfeng
	 * @date 2014-3-7 上午11:52:58
	 * @param mainParent
	 * @param node
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static Element getMainCloneChild(Element mainParent, Element node) {
		List<Element> children = mainParent.getChildren();
		for (int i = 0; i < children.size(); i++) {
			Element child = children.get(i);
			if (child.getName().equalsIgnoreCase(node.getName())) {
				if (!child.getName().equals(node.getName())) {
					logger.info("存在大小写不一致的同名节点：" + child.getName() + " <--> " + node.getName());
				}
				if (child.getAttributes().size() == 0) {
					return child;
				} else {
					String childId = child.getAttribute("id").getValue();
					String nodeId = node.getAttribute("id").getValue();
					if (childId == null || nodeId == null) {
						return null;
					}
					childId = childId.trim();
					nodeId = nodeId.trim();
					if (childId.equals(nodeId)) {
						logger.info("存在元素id相同的配置：" + childId);
					}
				}
			}
		}
		return null;

	}
	

	/**
	 * 
	 * @Description:将2个xml文本合并成为一个
	 * @author ganfeng
	 * @date 2014-3-7 下午12:05:44
	 * @param xml1
	 * @param xml2
	 * @return
	 */
	public static String combineXml(String xmlStr_1, String xmlStr_2) {
		if (StringUtils.isBlank(xmlStr_1) && !StringUtils.isBlank(xmlStr_2)) {
			return xmlStr_2;
		}
		if (!StringUtils.isBlank(xmlStr_1) && StringUtils.isBlank(xmlStr_2)) {
			return xmlStr_1;
		}
		if (StringUtils.isBlank(xmlStr_1) && StringUtils.isBlank(xmlStr_2)) {
			return "";
		}
		String newXML = "";
		try {
			Element element_1 = parseToElement(xmlStr_1);
			Element element_2 = parseToElement(xmlStr_2);
			Element newElemnet = combineElement(element_1, element_2);
			newXML = formatXml(newElemnet);
		} catch (Exception e) {
			logger.info("解释xml数据出错", e);
		}
		return newXML;
	}

	/**
	 * @Description:将两个Element合并成为一个
	 * @author ganfeng
	 * @date 2014-3-7 下午12:07:48
	 * @param el1
	 * @param el2
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Element combineElement(Element element_1, Element element_2) {
		Element _element_1 = (Element) element_1.clone();
		Element _element_2 = (Element) element_2.clone();
		List<Element> _element_2_Childs = _element_2.getChildren();
		for (int j = 0; j < _element_2_Childs.size();) {
			Element tmp = _element_2_Childs.get(0);
			tmp.detach();
			_element_1.addContent(tmp);
		}
		return _element_1;
	}

	

	/**
	 * @Description:格式化子节点文本输出---UTF-8格式
	 * @author ganfeng
	 * @date 2014-3-7 下午12:10:47
	 * @param element
	 * @return
	 */
	public static String formatXml(Element element) {
		XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
		String result = "";
		if (element != null) {
			result = outputter.outputString(element);
		}
		return result;
	}

	/**
	 * 
	 * @Description:通过xpath表达式获取root下子节点值
	 * @author ganfeng
	 * @date 2014-3-7 下午12:11:36
	 * @param exp
	 * @return
	 */
	public String xpathVal(String exp) {
		return JdomXMLUtil.xpathVal(root, exp);
	}

	/**
	 * 
	 * @Description: 通过xpath表达式获取值
	 * @author ganfeng
	 * @date 2014-3-7 下午12:13:10
	 * @param element  数据源元素
	 * @param exp xpath表达式
	 * @return
	 */
	public static String xpathVal(Element element, String exp) {
		String ret = "";
		try {
			Object obj = XPath.selectSingleNode(element, exp);
			if (obj == null) {
				return ret;
			}
			if (obj instanceof Text) {
				ret = ((Text) obj).getTextNormalize();
			} else if (obj instanceof Attribute) {
				ret = ((Attribute) obj).getValue();
			} else if (obj instanceof Element) {
				ret = ((Element) obj).getTextTrim();
			} else {
				ret = obj.toString();
			}

		} catch (Throwable e) {
			logger.info("xpath取值异常", e);
			logger.info("xpath表达式：" + exp);
			logger.info(formatXml(element));
		}
		return ret;
	}

	/**
	 * 
	 * @Description: 取得匹配表达式的第一个元素
	 * @author ganfeng
	 * @date 2014-3-7 下午12:15:15
	 * @param exp
	 * @return
	 */
	public Element xpathGetNode(String exp) {
		return xpthGetNode(root, exp);
	}

	/**
	 * 
	 * @Description: 取得匹配表达式的第一个元素
	 * @author ganfeng
	 * @date 2014-3-7 下午12:15:15
	 * @param exp
	 * @return
	 */
	public static Element xpthGetNode(Element element, String exp) {
		Element obj = null;
		try {
			obj = (Element) XPath.selectSingleNode(element, exp);
		} catch (JDOMException e) {
			logger.info("xpath[" + exp + "]取值异常", e);
		}
		return obj;
	}
	/**
	 * 
	 * @Description:取得匹配表达式的第一个元素的文本内容
	 * @author ganfeng
	 * @date 2014-3-10 下午02:17:08
	 * @param element
	 * @param exp
	 * @return
	 */
	public static String xpthGetNodeValue(Element element, String exp) {
		System.out.println(element + "--------");
		String ret = "";
		try {
			ret = ((Element) XPath.selectSingleNode(element, exp)).getTextTrim();
		} catch (JDOMException e) {
			System.out.println(1);
			logger.info("xpath[" + exp + "]取值异常", e);
		}
		return ret;
	}
	
	/**
	 * 
	 * @Description:通过子节点名称获取子节点元素的文本人内容
	 * @author ganfeng
	 * @date 2014-3-10 下午02:34:39
	 * @param element 父节点
	 * @param childName 子节点名
	 * @return
	 */
	public static String getChildValue(Element element, String childName){
		Element child = element.getChild(childName);
		if(child == null){
			return "";
		}
		return child.getTextTrim();
	}

	/**
	 * 
	 * @Description: 通过xpath表达式获取元素列表。
	 * @author ganfeng
	 * @date 2014-3-7 下午12:17:29
	 * @param exp
	 * @return
	 */
	public List<Element> xpathGetNodes(String exp) {
		return JdomXMLUtil.xpathGetNodes(root, exp);
	}

	/**
	 * 
	 * @Description:通过xpath表达式从一个元素中获取匹配的子元素列表。
	 * @author ganfeng
	 * @date 2014-3-7 下午12:18:20
	 * @param element 节点元素
	 * @param exp XPath表达式
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<Element> xpathGetNodes(Element element, String exp) {
		List<Element> nodes = new Vector<Element>();
		try {
			nodes = (List<Element>) XPath.selectNodes(element, exp);
		} catch (Throwable e) {
			logger.info("xpath取值异常", e);
			logger.info("xpath表达式：" + exp);
			logger.info(formatXml(element));
		}
		return nodes;
	}

	/**
	 * 
	 * @Description:格式化xml文本输出
	 * @author ganfeng
	 * @date 2014-3-7 下午12:19:50
	 * @param xmlText xml文本
	 * @return
	 */
	public static String formatXml(String xmlText) {
		JdomXMLUtil xmlJdom = new JdomXMLUtil();
		Element element = xmlJdom.parse(xmlText);
		String result = formatXml(element);
		return result;
	}
	

}
